#pragma once

#include <stddef.h>

#include <vector>

namespace hawking {
namespace indexlib {

// thread not safe
template<class Alloc>
class FixedSizeRecyclePool {
public:
    FixedSizeRecyclePool(size_t size, size_t num_per_alloc = 4 * 1024) :
        num_per_alloc_(num_per_alloc), free_(nullptr), end_(nullptr), free_list_(nullptr) {
        size_ = size > sizeof(void*) ? size : sizeof(void*);
        allocated_.reserve(1024);
    }
    FixedSizeRecyclePool(const FixedSizeRecyclePool&) = delete;
    FixedSizeRecyclePool& operator=(const FixedSizeRecyclePool&) = delete;

    ~FixedSizeRecyclePool() {
        for (char* buffer : allocated_) {
            allocator_.Deallocate(buffer, size_ * num_per_alloc_);
        }

        allocated_.clear();
        free_ = nullptr;
        end_ = nullptr;
        free_list_ = nullptr;
    }

    void* Allocate() {
        void* ret = nullptr;
        if (free_list_) {
            ret = free_list_;
            free_list_ = *static_cast<void**>(free_list_);
        } else if (free_ < end_) {
            ret = free_;
            free_ += size_;
        } else {
            char* buf = (char*)allocator_.Allocate(size_ * num_per_alloc_);
            if (!buf) {
                return nullptr;
            }

            ret = buf;
            free_ = buf +size_;
            end_ = buf + size_ * num_per_alloc_;
            allocated_.push_back(buf);
        }

        return ret;
    }

    void Free(void* ptr) {
        *reinterpret_cast<void**>(ptr) = free_list_;
        free_list_ = ptr;
    }

private:
    Alloc allocator_;
    size_t size_;
    size_t num_per_alloc_;

    char* free_;
    char* end_;
    void* free_list_;
    std::vector<char*> allocated_;
};

}
}
